package com.leo.tool.jdk.jdk4.nio;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

@Slf4j
public class Server {

    public static void main(String[] args) throws IOException {
        Selector acceptSelector = Selector.open();
        Selector executeSelector = Selector.open();

        ServerSocketChannel channel = ServerSocketChannel.open();
        channel.bind(new InetSocketAddress(8000));
        channel.configureBlocking(false);    // 设置为非阻塞模式
        channel.register(acceptSelector, SelectionKey.OP_ACCEPT);    // 监听ACCEPT事件

        // 一个线程接收连接
        new Thread(() -> accept(acceptSelector, executeSelector)).start();
        // 一个线程处理任务
        new Thread(() -> execute(executeSelector)).start();
    }

    @SneakyThrows(IOException.class)
    private static void accept(Selector acceptSelector, Selector executeSelector) {
        while (true) {
            // 不断轮询准备就绪的channel,不同于BIO的accept,非阻塞
            // timeout:
            //      0:阻塞等待事件完成;            如nginx,用一个线程专门监听所有io事件
            //      非0:设置轮询事件等待超时时间;    如redis为单线程,设置轮询时间避免阻塞
            if (acceptSelector.select() > 0) {
                Set<SelectionKey> keys = acceptSelector.selectedKeys();
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isAcceptable()) {
                        SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
                        channel.configureBlocking(false);
                        channel.register(executeSelector, SelectionKey.OP_READ);

                        // 移除
                        iterator.remove();
                    }
                }
            }
        }
    }

    @SneakyThrows(IOException.class)
    private static void execute(Selector executeSelector) {
        while (true) {
            // 不断轮询准备就绪的channel（已完成数据传输，数据从操作系统到了应用系统）
            // 与AIO区别，AIO不需要轮询检查数据是否准备就绪，而是由操作系统回调处理数据的函数，可通过线程池获取线程去操作
            if (executeSelector.select() > 0) {
                Set<SelectionKey> keys = executeSelector.selectedKeys();
                Iterator<SelectionKey> iterator = keys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if (key.isReadable()) {
                        SocketChannel channel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        channel.read(buffer);
                        buffer.flip();
                        log.info(Charset.defaultCharset().decode(buffer).toString());

                        // 移除
                        iterator.remove();
                    }
                }
            }
        }
    }
}
